﻿using System.Collections.Concurrent;

namespace System.NETool;

/// <summary>
/// 双主键线程安全字典，适用场景主键和键值都唯一，可双向查找
/// </summary>
/// <typeparam name="TKey">主键类型</typeparam>
/// <typeparam name="TValue">值类型</typeparam>
public class ConcurrentDoubleKeyDictionary<TKey, TValue> where TKey : notnull where TValue : notnull
{
    private readonly ConcurrentDictionary<TKey, TValue> _kv = new();
    private readonly ConcurrentDictionary<TValue, TKey> _vk = new();

    /// <summary>
    /// 插入或更新键值对
    /// </summary>
    /// <param name="key">主键</param>
    /// <param name="value">值</param>
    public void Upsert(TKey key, TValue value)
    {
        _kv[key] = value;
        _vk[value] = key;
    }

    /// <summary>
    /// 插入或更新键值对
    /// </summary>
    /// <param name="value">值</param>
    /// <param name="key">主键</param>
    public void Upsert(TValue value, TKey key)
    {
        _kv[key] = value;
        _vk[value] = key;
    }

    /// <summary>
    /// 通过主键获取或设置值
    /// </summary>
    /// <param name="key">主键</param>
    /// <returns>值</returns>
    public TValue this[TKey key]
    {
        get => GetValue(key, out var value) ? value : default;
        set => Upsert(key, value);
    }

    /// <summary>
    /// 获取字典中键值对的数量
    /// </summary>
    public int Count => _kv.Count;

    /// <summary>
    /// 通过主键移除键值对
    /// </summary>
    /// <param name="key">主键</param>
    /// <returns>是否成功移除</returns>
    public bool RemoveByKey(TKey key) => _kv.TryRemove(key, out var value) && _vk.TryRemove(value, out _);

    /// <summary>
    /// 通过值移除键值对
    /// </summary>
    /// <param name="value">值</param>
    /// <returns>是否成功移除</returns>
    public bool RemoveByValue(TValue value) => _vk.TryRemove(value, out var key) && _kv.TryRemove(key, out _);

    /// <summary>
    /// 通过主键获取值
    /// </summary>
    /// <param name="key">主键</param>
    /// <param name="value">值</param>
    /// <returns>是否成功获取</returns>
    public bool GetValue(TKey key, out TValue value) => _kv.TryGetValue(key, out value);

    /// <summary>
    /// 通过值获取主键
    /// </summary>
    /// <param name="value">值</param>
    /// <param name="key">主键</param>
    /// <returns>是否成功获取</returns>
    public bool GetKey(TValue value, out TKey key) => _vk.TryGetValue(value, out key);

    /// <summary>
    /// 判断字典是否包含指定主键
    /// </summary>
    /// <param name="key">主键</param>
    /// <returns>是否包含</returns>
    public bool ContainsKey(TKey key) => _kv.ContainsKey(key);

    /// <summary>
    /// 判断字典是否包含指定值
    /// </summary>
    /// <param name="value">值</param>
    /// <returns>是否包含</returns>
    public bool ContainsValue(TValue value) => _vk.ContainsKey(value);

    /// <summary>
    /// 获取所有主键
    /// </summary>
    /// <returns>主键集合</returns>
    public ICollection<TKey> Keys() => _kv.Keys;

    /// <summary>
    /// 获取所有值
    /// </summary>
    /// <returns>值集合</returns>
    public ICollection<TValue> Values() => _vk.Keys;

    /// <summary>
    /// 清空字典
    /// </summary>
    public void Clear()
    {
        _kv.Clear();
        _vk.Clear();
    }
}
